import { useMap } from 'ahooks';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ColGroups } from './ColGroup';
import { Header } from './Header';
import { Row } from './Row';
import { defaultBatchRenderSize, defaultSorterDirections, emptyAry, invliadRenderDataLength, noop } from './const';
import styles from './index.module.scss';
import { FocusCell, IColumn } from './types';
import { keyboardKey2direction, moveXy, useColFixed } from './util';
import { Footer } from './Footer';

interface IProps<T = any> {
  /**
   * 表格列配置
   * @warning 每一列必须设置宽度，最后一列宽度自适应
   */
  columns: IColumn<T>[];
  /**
   * 表格数据
   */
  data: T[];
  /**
   * 行key，必须保证每行唯一，类似id
   */
  rowKey: string;
  /**
   * 滚动设置
   * @warning 既设置滚动区域的高度，也设置滚动加载的相关参数来达到初始渲染的优化
   */
  scroll?: {
    /** 横向滚动 */
    x?: number;
    /** 竖直滚动高度 */
    y: number;
    /** 初始化渲染数据数量 */
    initRenderSize?: number;
    /**
     * 设置 `initRenderSize` 后，后续数据会滚动加载，设置一个滚动加载量,
     * @default 100
     */
    batchRenderSize?: number;
    /**
     * 距离底部距离小于该值，新增 `batchRenderSize` 数据加载
     * @default scroll.y
     */
    renderOffset?: number;
  };
  /**
   * 单元格输入触发，input输入值改变时触发
   * @param dataRow 当前渲染的数据行
   * @param dataKey 数据的key
   * @param val 编辑输入框的值
   * @param val 数据行key
   * @returns
   */
  onCellChange?: (dataRow: number, dataKey: string | number, val: any, rowKey: any) => void;
  /**
   * 单元格失去焦点时触发
   * @param dataRow 当前渲染的数据行
   * @param dataKey 数据的key
   * @param val 编辑输入框的值
   * @returns
   */
  onBlur?: (dataRow: number, dataKey: string | number, val: any, rowKey: any) => void;
  /**
   * 选中配置
   */
  rowSelection?: {
    /** 选中值 */
    selectedKeys: string[];
    /** 选中值变化 */
    onChange(keys: string[] | number[]): void;
    /** 指定选中的唯一字段 */
    key: string;
    /** checkbox属性 */
    checkboxProps?: (record: any) => {
      [key: string]: any;
    } | null;
  },
  /**
   * 是否显示footer
   */
  showFooter?: boolean
  /**
   * 是否显示footer
   */
  style?: React.CSSProperties;
}

/**
 * 自定义table，
 * - 单元格可编辑
 * - 单元格高度不支持配置
 * @warning best practice
 * - 不必要的功能不要再表格中拓展，保证组件高抽象, e.g. 表格统计和分页可以单独放置再组件内，通过给组件传参来控制其行为
 * - 配置项中的常量不要重复生成
 * - 配置项中不要添加额外的计算，e.g: width: 20 * 3
 * - 编辑时修改某个单元格数据，不要将每一行都重新生成，防止大量重渲染
 */
const VTable = memo(function <T>(props: IProps<T>) {
  const { columns: originColumns, data, rowKey, scroll, onCellChange, rowSelection, showFooter, style, onBlur } = props;
  // 更新表格行状态
  const [focusRows, { set, remove }] = useMap<number, FocusCell>();
  // 聚焦的单元格
  const focusCell = useRef<[number, number] | undefined>();
  // 是否正编辑
  const isEdit = useRef(false);
  const editableCols = useRef<string[]>([]);
  const maxRow = useRef<number>(-1);
  // 渲染的数据
  const [renderDataLength, setRenderDataLength] = useState(invliadRenderDataLength);

  const tbody = useRef<HTMLDivElement>(null);
  const theadWrapper = useRef<HTMLDivElement>(null);
  const tfooterWrapper = useRef<HTMLDivElement>(null);
  const renderDataLengthRef = useRef(renderDataLength);
  renderDataLengthRef.current = renderDataLength;
  const scrollRef = useRef(scroll);
  const selectedRef = useRef<string[]>([]);
  selectedRef.current = rowSelection ? rowSelection.selectedKeys : emptyAry;

  useEffect(() => {
    scrollRef.current = scroll;
  }, [scroll]);

  /** 检查聚焦编辑单元格是否可见 */
  // const checkFocusCellVisible = useCallback((row: number) => {
  //   // 有滚动+没有编辑时检查
  //   if (scrollRef.current && scrollRef.current.y) {
  //     const rowEle = tbody.current!.querySelector(`tr:nth-child(${row + 1})`);
  //     const { scrollTop, clientHeight } = tbody.current!;
  //     const minIndex = Math.ceil(scrollTop / cellHeight);
  //     const maxIndex = Math.ceil((scrollTop + clientHeight) / cellHeight);
  //     if (row <= minIndex) {
  //       tbody.current!.scrollTop -= scrollRef.current.y / 2;
  //     } else if (row >= maxIndex) {
  //       tbody.current!.scrollTop += scrollRef.current.y / 2;
  //     }
  //   }
  // }, []);

  // 滚动加载
  const loadMore = useCallback(() => {
    const { scrollHeight, scrollTop, clientHeight, scrollLeft } = tbody.current!;
    theadWrapper.current!.scrollLeft = scrollLeft; // 表头表体滚动同步
    if (tfooterWrapper.current) {
      tfooterWrapper.current!.scrollLeft = scrollLeft; // 表尾表体滚动同步
    }
    const { y, batchRenderSize = defaultBatchRenderSize, renderOffset } = scrollRef.current!;
    // 距离底部
    const distance = scrollHeight - scrollTop - clientHeight;
    if (distance <= (renderOffset || y)) {
      if (renderDataLengthRef.current < maxRow.current + 1) {
        setRenderDataLength((prev) => prev + batchRenderSize);
      }
    }
  }, [theadWrapper, tfooterWrapper]);

  useEffect(() => {
    // 仅initRenderSize修改时，从头开始渲染
    if (scroll && scroll.y && scroll.initRenderSize) {
      setRenderDataLength(scroll.initRenderSize);
    } else {
      setRenderDataLength(invliadRenderDataLength);
    }
  }, [scroll?.initRenderSize, scroll?.y]);

  // 平铺列配置
  const columns = useMemo(() => {
    const cols: IColumn<any>[] = [];
    const each = (ary: IColumn<any>[]) => {
      ary.forEach((d) => {
        if (d.children && d.children.length > 0) {
          each(d.children);
        } else {
          cols.push(d);
        }
      });
    };
    each(originColumns);
    return cols;
  }, [originColumns]);
  const colsRef = useRef(columns);
  colsRef.current = columns;

  // 更新可编辑的列
  useEffect(() => {
    let editableKeys: any[] = [];
    columns.forEach((d) => {
      if (d.editable) {
        editableKeys.push(d.key);
      }
    });
    editableCols.current = editableKeys;
  }, [columns]);

  useEffect(() => {
    maxRow.current = data.length - 1;
    if (focusCell.current) {
      const [row] = focusCell.current;
      // 数据聚焦行数超出
      if (row > maxRow.current) {
        remove(row);
        focusCell.current = undefined;
        isEdit.current = false;
      }
    }
  }, [data]);

  // 移动聚焦的表格项
  const moveFocusCell = useCallback((evt: KeyboardEvent) => {
    // 如果当前没有已聚焦的表格项，代表不能移动聚焦
    if (!focusCell.current) return;
    const maxCol = editableCols.current.length - 1; // 最大列
    const prev = focusCell.current;
    const moveDirection = keyboardKey2direction[evt.key as keyof typeof keyboardKey2direction];
    const newRc = moveXy(prev, moveDirection, [0, maxRow.current], [0, maxCol]);
    if (newRc[0] !== focusCell.current[0]) {
      remove(focusCell.current[0]); // 移除之前的聚焦行
      // checkFocusCellVisible(newRc[0]);
    }
    // 更新聚集行
    focusCell.current = newRc;
    set(newRc[0], {
      colKey: editableCols.current[newRc[1]]!,
      edit: isEdit.current,
    });
    // 存在移动时，禁止默认行为
    if (moveDirection) {
      evt.preventDefault();
    }
  }, []);

  useEffect(() => {
    document.addEventListener('keydown', moveFocusCell);
    return () => {
      document.removeEventListener('keydown', moveFocusCell);
    };
  }, [moveFocusCell]);

  // 进入编辑状态
  const enterEdit = useCallback((evt: KeyboardEvent) => {
    if (!focusCell.current) return;
    // 存在聚焦
    if (evt.key === 'Enter') {
      const prev = focusCell.current;
      // 正在编辑，移动到下一行
      if (isEdit.current) {
        if (prev[0] < maxRow.current) {
          remove(prev[0]);
          const newRow = prev[0] + 1;
          focusCell.current = [newRow, prev[1]];
          set(newRow, {
            edit: true,
            colKey: editableCols.current[prev[1]],
          });
        }
      } else {
        // 进入编辑
        isEdit.current = true;
        set(prev[0], {
          edit: true,
          colKey: editableCols.current[prev[1]],
        });
      }
    }
  }, []);

  useEffect(() => {
    document.addEventListener('keydown', enterEdit);
    return () => {
      document.removeEventListener('keydown', enterEdit);
    };
  }, [enterEdit]);

  const bodyStyle = useMemo(() => {
    if (scroll)
      return {
        maxHeight: scroll.y
      };
  }, [scroll]);

  // 单元格点击
  const onCellClick = useCallback((index: number, colKey: string | number, isDbClick = false) => {
    if (focusCell.current && index !== focusCell.current[0]) {
      remove(focusCell.current[0]);
    }
    set(index, {
      edit: isDbClick,
      colKey: colKey,
    });
    isEdit.current = isDbClick;
    focusCell.current = [index, editableCols.current.findIndex((d) => d === colKey)];
  }, []);

  const isScrollLoad = renderDataLength !== invliadRenderDataLength;

  // 移除聚焦的单元格
  const onEditBlur = useCallback(() => {
    if (focusCell.current) {
      remove(focusCell.current[0]);
      focusCell.current = undefined;
    }
  }, []);

  useEffect(() => {
    document.addEventListener('click', onEditBlur);
    return () => {
      document.removeEventListener('click', onEditBlur);
    };
  }, [onEditBlur]);

  const colFixed = useColFixed(columns);

  const showCheckbox = !!rowSelection;

  const [selectAll, indeterminate] = useMemo(() => {
    let selectAll = true;
    let indeterminate = false;
    if (rowSelection?.selectedKeys) {
      const map = rowSelection.selectedKeys.reduce((acc, key) => {
        acc[key] = true;
        return acc;
      }, {} as Record<string, boolean>);
      const key = rowSelection!.key;
      data.forEach((d) => {
        const id = (d as any)[key];
        //disabled 不需要不需要判断 排除即可
        const checkboxProps = rowSelection?.checkboxProps && rowSelection?.checkboxProps(d) || {}
        if (!checkboxProps.disabled) {
          if (map[id]) {
            indeterminate = true;
          } else {
            selectAll = false;
          }
        }
      });
    } else {
      selectAll = false;
    }
    return [selectAll, indeterminate];
  }, [rowSelection?.selectedKeys, rowSelection?.key, rowSelection?.checkboxProps, data]);

  const onRowSelect = useCallback((key: string) => {
    const newSelectedKeys = selectedRef.current.includes(key)
      ? selectedRef.current.filter(d => d !== key)
      : selectedRef.current.concat(key);
    rowSelection?.onChange(newSelectedKeys);
  }, [rowSelection?.onChange]);

  const onSelectAll = useCallback((checked: boolean) => {
    const key = rowSelection?.key;
    rowSelection?.onChange(
      checked ? data.map(d => {
        //disabled 不应该选中
        const checkboxProps = rowSelection?.checkboxProps && rowSelection?.checkboxProps(d) || {}
        if (checkboxProps.disabled) {
          return
        } else {
          return (d as any)[key as any]
        }
      }) : []
    );

  }, [rowSelection?.onChange, rowSelection?.key, rowSelection?.checkboxProps]);

  const [sorter, setSorter] = useState<{ colKey?: string | number; direction?: 'ascend' | 'descend' }>({});
  const sorterRef = useRef(sorter);
  sorterRef.current = sorter;

  const onSort = useCallback((col: string | number) => {
    const column = colsRef.current.find(d => d.key === col)!;
    const curSorter = sorterRef.current;
    const directions = (column.sortDirections || defaultSorterDirections);
    if (curSorter.colKey !== col) {
      setSorter({
        colKey: col,
        direction: directions[0] as any
      });
    } else {
      const directionIndex = directions.findIndex(d => d === curSorter.direction);
      const direction = !curSorter.direction
        ? directions[0]
        : directionIndex + 1 === directions.length
          ? undefined
          : directions[directionIndex + 1]
      setSorter({
        colKey: col,
        direction: direction as any
      });
    }
  }, []);

  const sortData = useMemo(() => {
    if (!sorter.colKey || !sorter.direction) return data;
    const compare = colsRef.current.find(d => d.key === sorter.colKey)!.sorter!;
    const cloneData = [...data];
    return cloneData.sort((a, b) => {
      return sorter.direction === 'ascend'
        ? compare(a, b) : -compare(a, b);
    });
  }, [data, sorter]);

  if (scroll?.y || scroll?.x) {
    return (
      <div className={styles.tableWrapper}>
        <div ref={theadWrapper} className={styles.tHeadWrapper}>
          <table className={styles.table} border={0} cellSpacing={0} cellPadding={0} style={{ ...style }}>
            <ColGroups columns={columns} showCheckbox={showCheckbox} />
            <Header sortKey={sorter.colKey} sortDirection={sorter.direction} onSort={onSort} columns={originColumns} colFixed={colFixed} showCheckbox={showCheckbox} indeterminate={indeterminate} selectAll={selectAll} onSelectAll={onSelectAll} />
          </table>
        </div>
        <div onScroll={isScrollLoad ? loadMore : noop} ref={tbody} className={styles.tbodyWrapper} style={bodyStyle}>
          <table className={styles.table} border={0} cellSpacing={0} cellPadding={0} style={{ ...style }}>
            <ColGroups columns={columns} showCheckbox={showCheckbox} />
            <tbody>
              {(isScrollLoad ? sortData.slice(0, renderDataLength) : data).map((d: any, idx) => {
                const selectedId = rowSelection ? d[rowSelection.key] : undefined;
                const checkboxProps = rowSelection?.checkboxProps && rowSelection.checkboxProps(d)
                return (
                  <Row
                    key={d[rowKey]}
                    index={idx}
                    onCellChange={onCellChange}
                    onBlur={onBlur}
                    onCellClick={onCellClick}
                    focus={focusRows.get(idx)}
                    data={d}
                    onEditBlur={onEditBlur}
                    columns={columns}
                    colFixed={colFixed}
                    showCheckbox={showCheckbox}
                    selectedId={selectedId}
                    onChecked={onRowSelect}
                    selected={rowSelection?.selectedKeys.includes(selectedId)}
                    checkboxProps={checkboxProps}
                  />
                );
              })}
            </tbody>
          </table>
        </div>
        {
          (showFooter && data.length > 0) &&
          <div ref={tfooterWrapper} className={styles.tbodyWrapper} style={{ maxHeight: 40, overflowX: 'hidden', marginLeft: rowSelection ? '43px': '0px' }}>
            <table className={styles.table} border={0} cellSpacing={0} cellPadding={0} style={{ ...style }} >
              <ColGroups columns={columns} showCheckbox={false} />
              <tbody>
                <Footer data={data} columns={originColumns} colFixed={colFixed} />
              </tbody>
            </table>
          </div>
        }
      </div>
    );
  }

  return (
    <>
      <table className={styles.table} border={0} cellSpacing={0} cellPadding={0} style={{ ...style }}>
        <ColGroups columns={columns} showCheckbox={showCheckbox} />
        <Header sortKey={sorter.colKey} sortDirection={sorter.direction} onSort={onSort} columns={originColumns} colFixed={colFixed} showCheckbox={showCheckbox} indeterminate={indeterminate} selectAll={selectAll} onSelectAll={onSelectAll} />
        <tbody>
          {(isScrollLoad ? sortData.slice(0, renderDataLength) : data).map((d: any, idx) => {
            const selectedId = rowSelection ? d[rowSelection.key] : undefined;
            const checkboxProps = rowSelection?.checkboxProps && rowSelection.checkboxProps(d)
            return (
              <Row
                key={d[rowKey]}
                index={idx}
                onCellChange={onCellChange}
                onBlur={onBlur}
                onCellClick={onCellClick}
                focus={focusRows.get(idx)}
                onEditBlur={onEditBlur}
                data={d}
                columns={columns}
                colFixed={colFixed}
                showCheckbox={showCheckbox}
                onChecked={onRowSelect}
                selectedId={selectedId}
                selected={rowSelection?.selectedKeys.includes(selectedId)}
                checkboxProps={checkboxProps}
              />
            );
          })}
        </tbody>
      </table>
      {
        (showFooter && data.length > 0) &&
        <table className={styles.table} border={0} cellSpacing={0} cellPadding={0} style={{ ...style }}>
          <ColGroups columns={columns} showCheckbox={false} />
          <tbody>
            <Footer data={data} columns={originColumns} colFixed={colFixed} />
          </tbody>
        </table>
      }
    </>

  );
});

export * from './types';
export { VTable };

/**
 * TODO:
 * 横向滚动设置
 */
